import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { defer, of, map, concatMap, delay, firstValueFrom } from "rxjs";
import * as puppeteer from "puppeteer"
import axios from "axios";
import * as cheerio from "cheerio"
import * as moment from "moment"
import { DbService } from "@/db/db.service";
import { resolve } from "path";
import IGlobal from "@/services/global.service";
import IHelper from "@/services/helper.service";
import { IWonewSer } from "@/typing";
import ILogger from "@/services/logger.service";

@Injectable()
export class KsUserActionHookService {
  logPrefix: string = ""
  constructor(
    private readonly dbService: DbService,
  ) {}

  getCachedKey(type: string, accountId: number): string {
    return `hook_user_action_${type}_${accountId}`
  }
  
  log(message: string) {
    ILogger.info(`${this.logPrefix} ${message}`)
  }

  async fire(accountId: number, logPrefix?: string) {
    this.logPrefix = logPrefix || "[KsUserActionHookService 养号]"
    this.log("fire")
    const cachedKey = this.getCachedKey("service", accountId)
    if (IGlobal.store[cachedKey]) {
      this.log("Service is running! return!")
      return {status: false, message: "Service is running!"}
    }
    if (!await this.isServiceAvailable(accountId)) {
      this.log("Service not available! return!")
      return {status: false, message: "Service not available!"}
    }
    this.log("Service starting!")
    this.flushPage(accountId)
    return {status: true, message: "Service start!"}
  }

  async getCookie(accountId: number): Promise<puppeteer.Cookie[]> {
    let node = await this.dbService.accountRepository.findOneByOrFail({id: accountId})
    if (!node.cookie) {
      throw new HttpException(`Account cookie not exist`, HttpStatus.BAD_REQUEST)
    }
    return JSON.parse(node.cookie)
  }

  async getBrowser(accountId: number): Promise<puppeteer.Browser> {
    const cachedKey = this.getCachedKey("browser", accountId)
    let cachedBrowser: puppeteer.Browser | undefined = IGlobal.store[cachedKey]
    if (!cachedBrowser) {
      cachedBrowser = await puppeteer.launch({
        headless: false,
        defaultViewport: {
          width: 1280, height: 720
        },
        devtools: false,
        protocolTimeout: 10 * 60 * 1000,
        args: ['--no-sandbox','--disable-setuid-sandbox', "--window-size=1280,1000",],
      })
      IGlobal.store[cachedKey] = cachedBrowser
    }
    return cachedBrowser
  }

  async getHomePage(accountId: number, browser: puppeteer.Browser): Promise<puppeteer.Page> {
    const cachedKey = this.getCachedKey("home_page", accountId)
    let page: puppeteer.Page | undefined = IGlobal.store[cachedKey]
    if (!page) {
      page = await browser.newPage()
      const cookies = await this.getCookie(accountId)
      //设置cookie
      for(let i = 0; i < cookies.length; i++){
        await page.setCookie(cookies[i])
      }
      await page.goto("https://www.douyin.com/", {waitUntil: "domcontentloaded"})
      IGlobal.store[cachedKey] = page
    }
    return page
    
  }

  async isServiceAvailable(accountId: number, destroy?: boolean): Promise<boolean> {
    let accountAvailable = false
    try {
      const account = await this.dbService.accountRepository.findOneByOrFail({id: accountId})
      accountAvailable = account.hook_user_action === 1
    } catch(e) {}
    const timeAvailable = IHelper.hookTimeAvailable()
    const serviceAvailable = accountAvailable && timeAvailable
    if (!serviceAvailable && destroy) {
      await this.destroyService(accountId)
    }
    return serviceAvailable
  }

  async destroyService(accountId: number) {
    const serviceKey = this.getCachedKey("service", accountId)
    const browserKey = this.getCachedKey("browser", accountId)
    const homepageKey = this.getCachedKey("home_page", accountId)
    IGlobal.store[serviceKey] = undefined
    if (IGlobal.store[browserKey]) {
      await IGlobal.store[browserKey].close().catch(e => e)
    }
    IGlobal.store[browserKey] = undefined
    IGlobal.store[homepageKey] = undefined
    this.log("Service destroy!")
  }

  async flushPage(accountId: number) {
    if (!await this.isServiceAvailable(accountId, true)) return
    this.log(`Start hook user action!`)
    IGlobal.store[this.getCachedKey("service", accountId)] = this
    try {
      const browser = await this.getBrowser(accountId)
      const homePage = await this.getHomePage(accountId, browser)
      await homePage.setRequestInterception(true)
      homePage.on('request', (interceptedRequest: puppeteer.HTTPRequest) => {
        if (interceptedRequest.url().includes("https://verify.zijieapi.com/captcha/get")) {
          this.log(`需要验证，刷新页面. Destroy service!!`)
          this.destroyService(accountId)
          interceptedRequest.abort()
          // ILogger.info("需要验证，刷新页面")
          // homePage.reload()
        } else {
          interceptedRequest.continue()
        }
        
      })
      homePage.on('response', async (res: puppeteer.HTTPResponse) => {
        if (res.request().url().includes("https://www.douyin.com/aweme/v1/web/history/write")) {
          const params = IHelper.parseQueryStr(res.request().postData() || "")
          await homePage.evaluate((params) => {
            // @ts-ignore
            if (window.$ && window.$.get) {
              // @ts-ignore
              window.$.get(`https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=${params.aweme_id}`)
            }
          }, params)
        }
        if (res.request().url().includes("https://www.douyin.com/aweme/v1/web/aweme/detail")) {
          try {
            const available = await this.isServiceAvailable(accountId, true)
            if (!available) {
              this.log(`开始下个视频时判断 Service not available. Destroy service!!`)
            }
            if (available) {
              const data = await res.json()
              // @ts-ignore
              let message = `开始播放视频 id:${data.aweme_detail.aweme_id} ${data.aweme_detail.desc} 时长：${moment.duration(data.aweme_detail.duration, "milliseconds").format("HH:mm:ss", {trim: false})}`
              this.log(message)
              if (data.aweme_detail.duration > 300 * 1000) {
                setTimeout(() => {
                  homePage.keyboard.press("ArrowDown")
                }, 300 * 1000)
              }
              if ((+data.aweme_detail.aweme_id) % 2 === 0) {
                homePage.keyboard.press("z")
              }
            }
            
          } catch(e) {}
        }
      })
      await this.sleep(20 * 1000)
      await homePage.evaluate(() => {
        localStorage.setItem("douyin_web_hide_guide", "1")
        localStorage.setItem("is_allow_trust", "2")
        localStorage.setItem("player_nextautoplay", "\"1\"")
        localStorage.setItem("video_can_play_type", "true")
        localStorage.setItem("player_is_mute", "0")
        localStorage.setItem("browserMute", "false")
      })
      await homePage.reload()
      await homePage.evaluate(() => {
        var script = document.createElement('script');
        script.src = 'https://libs.baidu.com/jquery/1.9.1/jquery.min.js';
        document.head.appendChild(script);
      })
      const btn = await homePage.waitForSelector(".xgplayer-volume")
      this.log("Service started")
      return {t: new Date()}
    } catch (e) {}
    
    
    // await this.flushPage(accountId)
  }

  sleep(time: number): Promise<number> {
    return new Promise<number>(resolve => {
      setTimeout(() => {
        resolve(1)
      }, time)
    })
  }
}